package com.btl.component.oss.service;

import com.btl.component.oss.env.OssProperties;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.time.Duration;

@Slf4j
@Component
public class OssService {

    @Resource
    private S3Presigner boatS3Presigner;

    @Resource
    private S3Client boatS3Client;

    @Resource
    private OssProperties ossProperties;

    /**
     * 获取上传URL
     *
     * @param key 存储key
     * @return String
     */
    public String getUploadUrl(String key) {
        log.info("获取上传签名-bucket:{},key:{}-开始", ossProperties.getBucketName(), key);
        AwsRequestOverrideConfiguration override = AwsRequestOverrideConfiguration.builder()
                .putRawQueryParameter("x-amz-acl", ObjectCannedACL.PUBLIC_READ.toString())
                .build();
        PutObjectRequest objectRequest = PutObjectRequest.builder()
                .bucket(ossProperties.getBucketName())
                .key(key)
                .overrideConfiguration(override)
                .build();
        log.info("获取上传签名参数-bucket:{},key:{},request:{}", ossProperties.getBucketName(), key, objectRequest);
        PutObjectPresignRequest signRequest = PutObjectPresignRequest.builder()
                .signatureDuration(Duration.ofSeconds(30))  // The URL expires in 10 second.
                .putObjectRequest(objectRequest)
                .build();
        PresignedPutObjectRequest signedRequest = boatS3Presigner.presignPutObject(signRequest);
        log.info("获取上传签名-bucket:{},key:{},url:{}-结束", ossProperties.getBucketName(), key, signedRequest.url()
                .toExternalForm());
        return signedRequest.url()
                .toExternalForm();
    }

    /**
     * 获取下载URL权限
     *
     * @param key 存储key
     * @return String
     */
    public String getDownloadUrl(String key) {
        String preSignatureUrl = "";
        try {
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                    .bucket(ossProperties.getBucketName())
                    .key(key)
                    .build();
            //设置预签名URL可访问时间 1天
            GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofSeconds(30))
                    .getObjectRequest(getObjectRequest)
                    .build();
            PresignedGetObjectRequest presignedGetObjectRequest = boatS3Presigner.presignGetObject(getObjectPresignRequest);
            preSignatureUrl = String.valueOf(presignedGetObjectRequest.url());
        } catch (Exception e) {
            log.error("获取上传URL权限失败", e);
        }
        return preSignatureUrl;
    }

    /**
     * 上传
     * 注意需要配置禁用校验
     *
     * @param key           key
     * @param contentType   文件类型
     * @param contentLength 文件大小
     * @param inputStream   文件流
     */
    public void uploadByInputStream(String key, String contentType, long contentLength, InputStream inputStream) {
        PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                .key(key)
                .bucket(ossProperties.getBucketName())
                .contentType(contentType)
                .acl(ObjectCannedACL.PUBLIC_READ)
                .build();
        RequestBody requestBody = RequestBody.fromInputStream(inputStream, contentLength);
        boatS3Client.putObject(putObjectRequest, requestBody);
    }

    /**
     * 上传
     *
     * @param key         key
     * @param contentType 文件类型
     * @param bytes       文件流
     */
    public void uploadByByteArray(String key, String contentType, byte[] bytes) {
        PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                .key(key)
                .bucket(ossProperties.getBucketName())
                .contentType(contentType)
                .acl(ObjectCannedACL.PUBLIC_READ)
                .build();
        RequestBody requestBody = RequestBody.fromBytes(bytes);
        boatS3Client.putObject(putObjectRequest, requestBody);
    }

    /**
     * 上传
     *
     * @param key key
     * @param url 文件URL
     */
    public void uploadByUrl(String key, String url) throws IOException {
        try {
            URL urls = new URI(url).toURL();
            try (InputStream inputStream = urls.openStream()) {
                URLConnection connection = urls.openConnection();
                String contentType = connection.getContentType();
                long contentLength = connection.getContentLengthLong();
                uploadByInputStream(key, contentType, contentLength, inputStream);
            }
        } catch (URISyntaxException | MalformedURLException e) {
            throw new IOException("URL格式错误", e);
        } catch (IOException e) {
            log.error("上传文件异常:{}", e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 修改对象存储key
     *
     * @param sourceKey      源key
     * @param destinationKey 新key
     */
    public void modOss(String sourceKey, String destinationKey) {
        boatS3Client.copyObject(builder ->
                builder.sourceBucket(ossProperties.getBucketName())
                        .sourceKey(ossProperties.getBucketName() + "/" + sourceKey)
                        .destinationBucket(ossProperties.getBucketName())
                        .destinationKey(destinationKey));
        boatS3Client.deleteObject(builder -> builder.bucket(ossProperties.getBucketName())
                .key(sourceKey));
    }
}
